/*
 * @(#)AdmRemoteBESA.java 2.0	11/01/11
 *
 * Copyright 2011, Pontificia Universidad Javeriana, All rights reserved.
 * Takina and SIDRe PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package BESA.Remote;

import BESA.ExceptionBESA;
import BESA.Kernell.Agent.AgentBESA;
import BESA.Remote.Directory.DistributedDirectoryExceptionBESA;
import BESA.Kernell.System.SystemExceptionBESA;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import BESA.Kernell.Agent.StateBESA;
import BESA.Kernell.Agent.StructBESA;
import BESA.Kernell.Agent.Event.EventBESA;
import BESA.Kernell.System.AdmBESA;
import BESA.Kernell.System.Directory.AgHandlerBESA;
import BESA.Log.ReportBESA;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class implements the mechanisms for the handling of RPC.
 * 
 * @author  SIDRe - Pontificia Universidad Javeriana
 * @author  Takina  - Pontificia Universidad Javeriana
 * @version 2.0, 11/01/11
 * @since   JDK1.0
 */
public class AdmRemoteImpBESA extends UnicastRemoteObject implements AdmRemoteInterfaceBESA, Serializable {

    /**
     *
     */
    public static final long serialVersionUID = "AdmRemote".hashCode();
    /**
     *
     */
    private static AdmBESA admLocal;

    /**
     * Constructs a new AdmRemote object.
     *
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public AdmRemoteImpBESA() throws RemoteException {
        super();
    }

    /**
     * Obtains the BESA local administrator associated.
     *
     * @return The BESA local administrator.
     */
    public AdmBESA getAdmLocal() {
        return admLocal;
    }

    /**
     * Synchronizes the infomaci�n of agents in the BESA containers assets.
     *
     * @param admId The identifier of the BESA container.
     * @param agIdList List of agents identifiers.
     * @param aliasList List of agents names.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public void synchronizeRemoteAgentDirectory(String admId, ArrayList agIdRemoteList, ArrayList aliasRemoteList) throws RemoteException {
        try {
            //actualizar los remotos en el directorio local
            ((RemoteAdmBESA) admLocal).updateAgentDirectory(admId, agIdRemoteList, aliasRemoteList);
        } catch (DistributedDirectoryExceptionBESA ex) {
            Logger.getLogger(AdmRemoteImpBESA.class.getName()).log(Level.SEVERE, null, ex);//TODO
        }
        //callback para update de los locales en el remoto
        ArrayList<String> idList = new ArrayList<String>();
        ArrayList<String> aliasList = new ArrayList<String>();
        //AdmRemoteBESA.admLocal.geLocalDirectoryList(idList, aliasList);
        //ESTA GENERANDO LA MISMA LISTA QUE ACABO DE RECIBIR
        //FUNCIONA PORQUE SON LISTAS VACIAS, COMO SE SOLUCIONA EL PROBLEMA
        // UTILIZAR EL ID DEL CONTENEDOR QUE INVOCA ESTE M�TODO REMOTO PARA
        //INVOCAR EL M�TODO AL OTRO LADO, Y SEGUNDA RETORNAR UN ARRAYLIST
        //PARA REGISTRAR LOS AGNETES DEL CONTENEDOR QUE ACABA DE SUBIR.
        this.updateRemoteAgentDirectory(AdmRemoteImpBESA.admLocal.getAdmHandler().getAdmId(), idList, aliasList);
    }

    /**
     * Updates the directories of identifiers and alias of agents in a remote
     * BESA container.
     *
     * @param admAlias Alias/name of the admnistrador.
     * @param agIdList List of agents identifiers.
     * @param agAliasList List of agents names.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public void updateRemoteAgentDirectory(String admId, ArrayList agIdList, ArrayList aliasList) throws RemoteException {
        try {
            ((RemoteAdmBESA)admLocal).updateAgentDirectory(admId, agIdList, aliasList);
        } catch (DistributedDirectoryExceptionBESA ex) {
            Logger.getLogger(AdmRemoteImpBESA.class.getName()).log(Level.SEVERE, null, ex);//TODO
        }
    }

    /**
     *
     * @param adIdList
     * @param aliasList
     * @throws RemoteException
     */
    public void importAgentDirectory(ArrayList<String> adIdList, ArrayList<String> aliasList) throws RemoteException {
        ((RemoteAdmBESA)admLocal).importAgentDirectory(adIdList, aliasList);
    }

    /**
     * Makes the remote registry of an agent in a BESA container.<BR><BR>
     *
     * Creates the aid card.<BR><BR>
     *
     * Does the agent lookup.
     *
     * @param agAlias Name of the agent with whom it will be identified in the
     * BESA container.
     * @param agId The identifier of the agent.
     * @param admId The identifier of the BESA container.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public void registerRemoteAgents(String agAlias, String agId, String admId) {
        try {
            ((RemoteAdmBESA)admLocal).registerRemoteAgent(agAlias, agId, admId);
        } catch (DistributedDirectoryExceptionBESA ex) {
            Logger.getLogger(AdmRemoteImpBESA.class.getName()).log(Level.SEVERE, null, ex);//TODO
        }
    }

    /**
     * Makes the registry of an remote BESA administrator.
     *
     * @param admAlias Alias/name of the admnistrador.
     * @param ipAddress The IP address of the machine that contains the
     * administrator to be registered.
     * @param rmiPort The port of the machine that contains the administrator
     * to be registered.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    @Override
    public void registerRemoteAdm(String admId, String admAlias, String ipAddress, int rmiPort) throws RemoteException {
        try {
            ((RemoteAdmBESA)admLocal).registerRemoteAdm(admId,admAlias, ipAddress, rmiPort);
        } catch (SystemExceptionBESA ex) {
            ex.printStackTrace();//TODO
        }
    }

    /**
     * Makes the remote registry of an agent in a BESA container.<BR><BR>
     *
     * Creates the aid card.<BR><BR>
     *
     * Does the agent lookup.
     *
     * @param password Password of the local administrator.
     * @param administratorName Name of the administrator.
     * @param aid Is a chain formed of the following way: IP + "." + port + "." + aid.
     * @return true if the registry can be made; false otherwise.<BR><BR>
     *
     * The registry of the agent can fail if the name of the administrator is
     * not registered in the administrators table or if appears problems when
     * doing the lookup (IP of the remote admnistrador, port of the remote
     * administrator, aid).
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public boolean registerRemoteAgent(String password, String administratorName, String aid) throws RemoteException {
        return true;
    }

    /**
     * Eliminates the remote registry of an agent. To do this, a null value is
     * asigned to the aid card.
     *
     * @param administratorName Name assigned to the administrator.
     * @param aid The identifier of the agent.
     * @param password Password of the local administrator.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public /*synchronized*/ void unregisterRemoteAgent(String password, String administratorName, String aid) throws RemoteException {
    }

    /**
     * Creates a remote service in a BESA container.
     *
     * @param servName The oficial name of the service to be published.
     * @param descriptors A group of alias that describes the service.
     * @param password Password of the local administrator.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public /*synchronized*/ void createRemoteService(String password, String servName, ArrayList descriptors) throws RemoteException {
    }

    /**
     * Binds a remote service associated to an agent.
     *
     * @param aid The identifier of the agent.
     * @param servName The oficial name of the service.
     * @param password Password of the local administrator.
     * @return
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public /*synchronized*/ boolean bindRemoteService(String password, String aid, String servName) throws RemoteException {
        return true;
    }

    /**
     * Sends an event to an agent.
     *
     * @param ev The event BESA to be sending.
     * @param aid The identifier of the agent.
     * @throws BESA.Exception.ExceptionBESA_SendMessageError
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     * @throws BESA.Exception.ExceptionBESA_SendMessageError An error may
     * happen while the event is being sending.
     */
    public void sendEvent(EventBESA ev, String aid) throws RemoteException {
        // Obtener tarjeta del destinatario
        // hacia el futuro se puede implantar un cache
        //se quita        AgHandlerBESA agh = (AgHandlerBESA)admLocal.getHandlerByAid(aid);
        AgHandlerBESA agh = null;
        try {
            agh = (AgHandlerBESA) admLocal.getHandlerByAid(aid);
        } catch (ExceptionBESA ex) {
            Logger.getLogger(AdmRemoteImpBESA.class.getName()).log(Level.SEVERE, null, ex);
        }
        if (agh == null) {
            // si el destinatario no existe -> send NO posible
            ReportBESA.error("The agent " + aid + " haven't remote handler: may be that agente is died.");
            throw new RemoteException("The agent " + aid + " haven't remote handler: may be that agente is died.");
        } //se quita else if (agh.getLocation()==agh.AG_LOCATION_LOCAL) {
        else {// if (agh.getAgHandlerBESA().getLocation()== AgHandlerBESA.AG_LOCATION_LOCAL) {
            try {
                //se quita ((AgLocalHandler)agh).getAg().sendEvent(ev);
                //((AgHandler)agh).getAg().sendEvent(ev);
                agh.sendEvent(ev);
            } catch (Exception e) {
                ReportBESA.error("Couldn't send the event to the remote agent " + aid + ":" + e.toString());
                throw new RemoteException("Couldn't send the event to the remote agent " + aid + ":" + e.toString());
            }
        }
        //        else {       	throw new ExceptionBESA_SendMessageError (ExceptionBESA_SendMessageError.EXCEPTION_NOTLOCALAGENT);        }

    }

    /**
     * Initializes the instance of the associated BESA administrator with control
     * of type singleton.
     *
     * @param adm The BESA administrator.
     */
    public static void initAdmLocal(AdmBESA adm) {
        // init de la var estatica con control tipo singleton
        if (admLocal == null) {
            admLocal = adm;
        }
    }

    /**
     * Moves an external agent to the associated local container.
     *
     * @param aliasAg Name of the agent with whom it will be identified in the
     * BESA container.
     * @param stateAg The agent associated state object.
     * @param structAg The agent associated structure.
     * @param passwdAg Agent password.
     * @param nameClassAgent A String that represents the agent associated class.
     * @throws java.rmi.RemoteException
     */
    @Override
    public void moveAgentReceive(String aliasAg, StateBESA stateAg, StructBESA structAg, double passwdAg, String nameClassAgent) throws RemoteException {
        try {
            //----------------------------------------------------------------//
            //
            //----------------------------------------------------------------//
            Class cls = Class.forName(nameClassAgent);
            Class[] partypes = new Class[4];
            partypes[0] = aliasAg.getClass();
            partypes[1] = StateBESA.class;//stateAg.getClass();
            partypes[2] = structAg.getClass();
            //Double password = new Double(passwdAg);
            partypes[3] = double.class;//password.getClass();
            Constructor ct = cls.getConstructor(partypes);
            //----------------------------------------------------------------//
            //
            //----------------------------------------------------------------//
            Object[] arglist = new Object[4];
            arglist[0] = aliasAg;
            arglist[1] = (StateBESA)stateAg;
            arglist[2] = structAg;
            arglist[3] = passwdAg;//new Double(passwdAg);
            Object agent = ct.newInstance(arglist);
            //----------------------------------------------------------------//
            //
            //----------------------------------------------------------------//
            ((AgentBESA) agent).start();            
        } catch (Exception e) {
            ReportBESA.error("Couldn't create a instance of the agent that is moving: " + e.toString());
            throw new RemoteException("Couldn't create a instance of the agent that is moving: " + e.toString());
        }
        //Ping para actualizar los directorios remotos
    }

    /**
     * Moves a local agent to an external BESA container.
     *
     * @param agId The identifier of the agent.
     * @param aliasDestinationAdm Alias/name of the destination admnistrador.
     * @param passwdAg Agent password.
     * @throws java.rmi.RemoteException Is generated by a number of
     * communication-related exceptions that may occur during the execution of
     * the remote method call.
     */
    public void moveAgentSend(String agId, String aliasDestinationAdm, double passwdAg) throws RemoteException {
        try {
            ((RemoteAdmBESA)admLocal).moveAgent(agId, aliasDestinationAdm, passwdAg);
        } catch (Exception e) {
            ReportBESA.error("Couldn't can move the agent " + agId + ": " + e.toString());
            throw new RemoteException("Couldn't move the agent " + agId + ": " + e.toString());
        }
    }

    @Override
    public void killRemoteAgent(String agId, double agentPassword) throws RemoteException {
        try {
            ((RemoteAdmBESA)admLocal).killAgent(agId, agentPassword);
        } catch (ExceptionBESA ex) {
            ReportBESA.error("Couldn't kill the agent " + agId + ": " + ex.toString());
            throw new RemoteException("Couldn't kill the agent " + agId + ": " + ex.toString());
        }
    }
}
